#ifndef JINSTERFACESTRATEGY_H
#define JINSTERFACESTRATEGY_H

#include <QTECore/qteglobal.h>
#include <QVariant>
#include <QObject>

#ifndef QTE_interface_register
# define QTE_interface_register(Type, name, ...) \
	QTE_type_register(Type, name, __VA_ARGS__)
#endif

#ifndef QTE_interface_register_id
# define QTE_interface_register_id(Type, user_id, ...) \
	QTE_type_register(Type, "ClassName_"#user_id, __VA_ARGS__)
#endif

namespace QTE
{

#if __cplusplus >= 202002L

namespace JInterfaceConcept
{

template <typename CallBack>
concept F = requires(CallBack f) {
	f();
};

template <typename CallBack>
concept FVV = requires(CallBack f) {
	f(std::declval<QVariant>());
};

template <typename CallBack>
concept FVA = requires(CallBack f) {
	f(std::declval<ToAnyType>());
};

template <typename Class, typename CallBack>
concept MF = requires(Class *obj, CallBack f) {
	(obj->*f)();
};

template <typename Class, typename CallBack>
concept MFVV = requires(Class *obj, CallBack f) {
	requires
	requires { (obj->*f)(std::declval<QVariant>()); } or
	requires { requires isBaseOfQObject<Class>::value; f(std::declval<QVariant>()); };
};

template <typename Class, typename CallBack>
concept MFVA = requires(Class *obj, CallBack f) {
	(obj->*f)(std::declval<ToAnyType>());
	requires
	requires { (obj->*f)(std::declval<QVariant>()); } or
	requires { requires isBaseOfQObject<Class>::value; f(std::declval<QVariant>()); };
};

} //namespace JInterfaceConcept

# define JINTERFACE_CONCEPT(C, ...)   requires JInterfaceConcept::C <__VA_ARGS__> inline auto

# define JINTERFACE_CONCEPT_VAR  JINTERFACE_CONCEPT(FVV, CallBack)
# define JINTERFACE_CONCEPT_ANY  JINTERFACE_CONCEPT(FVA, CallBack)
# define JINTERFACE_CONCEPT_IGN  JINTERFACE_CONCEPT(F, CallBack)

# define JINTERFACE_CONCEPT_VAR_OBJ  JINTERFACE_CONCEPT(MFVV, Class, CallBack)
# define JINTERFACE_CONCEPT_ANY_OBJ  JINTERFACE_CONCEPT(MFVA, Class, CallBack)
# define JINTERFACE_CONCEPT_IGN_OBJ  JINTERFACE_CONCEPT(MF, Class, CallBack)

#else // < c++20

namespace JInterfaceConcept
{

template <typename CallBack>
auto __requires(int) -> decltype(std::declval<CallBack>()(), std::true_type());

template <typename CallBack>
auto __requires(int) -> decltype(std::declval<CallBack>()(std::declval<ToAnyType>()), std::true_type());

template <typename Class, typename CallBack>
auto __requires(int) -> decltype((std::declval<Class>().*std::declval<CallBack>())(), std::true_type());

template <typename Class, typename CallBack>
auto __requires(int) -> decltype((std::declval<Class>().*std::declval<CallBack>())(std::declval<ToAnyType>()), std::true_type());

template <class Class, typename CallBack>
typename std::enable_if<
	isBaseOfQObject<Class>::value,
	decltype(std::declval<CallBack>()(), std::true_type())
>::type __requires(int);

template <class Class, typename CallBack>
typename std::enable_if<
	isBaseOfQObject<Class>::value,
	decltype(std::declval<CallBack>()(std::declval<ToAnyType>()), std::true_type())
>::type __requires(int);

template <typename CallBack> std::false_type __requires(...);
template <class Class, typename CallBack> std::false_type __requires(...);

} //namespace JInterfaceConcept

# define __JIS_JFUNCTIONTRAITS(cond)  inline typename std::enable_if<cond, void>::type

# define __JIS_SEI_CONDITION_VAR(...) \
	__JIS_JFUNCTIONTRAITS (( \
		JFunctionTraits<CallBack>::arity == 1 and \
		__VA_ARGS__ \
		( std::is_same< \
			typename JFunctionTraits<CallBack>::FunctionType, \
			typename JFunctionTraits<CallBack>::ResultType(QVariant) \
		>::value or \
		std::is_same< \
			typename JFunctionTraits<CallBack>::FunctionType, \
			typename JFunctionTraits<CallBack>::ResultType(const QVariant&) \
		>::value or \
		std::is_same< \
			typename JFunctionTraits<CallBack>::FunctionType, \
			typename JFunctionTraits<CallBack>::ResultType(QVariant&&) \
		>::value ) ))

# define __JIS_SEI_CONDITION_ANY(...) \
	__JIS_JFUNCTIONTRAITS (( \
		JFunctionTraits<CallBack>::arity == 1 and \
		__VA_ARGS__ \
		( not std::is_same< \
			typename JFunctionTraits<CallBack>::FunctionType, \
			typename JFunctionTraits<CallBack>::ResultType(QVariant) \
		>::value and \
		not std::is_same< \
			typename JFunctionTraits<CallBack>::FunctionType, \
			typename JFunctionTraits<CallBack>::ResultType(const QVariant&) \
		>::value and \
		not std::is_same< \
			typename JFunctionTraits<CallBack>::FunctionType, \
			typename JFunctionTraits<CallBack>::ResultType(QVariant&&) \
		>::value ) ))

# define JINTERFACE_CAN_CALL_VAL1  decltype(JInterfaceConcept::__requires<CallBack>(0))::value
# define JINTERFACE_CAN_CALL_VAL2  decltype(JInterfaceConcept::__requires<Class,CallBack>(0))::value

# define JINTERFACE_CONCEPT_VAR  __JIS_SEI_CONDITION_VAR(JINTERFACE_CAN_CALL_VAL1 and)
# define JINTERFACE_CONCEPT_ANY  __JIS_SEI_CONDITION_ANY(JINTERFACE_CAN_CALL_VAL1 and)
# define JINTERFACE_CONCEPT_IGN  __JIS_JFUNCTIONTRAITS(JINTERFACE_CAN_CALL_VAL1 and JFunctionTraits<CallBack>::arity == 0)

# define JINTERFACE_CONCEPT_VAR_OBJ  __JIS_SEI_CONDITION_VAR(JINTERFACE_CAN_CALL_VAL2 and)
# define JINTERFACE_CONCEPT_ANY_OBJ  __JIS_SEI_CONDITION_ANY(JINTERFACE_CAN_CALL_VAL2 and)
# define JINTERFACE_CONCEPT_IGN_OBJ  __JIS_JFUNCTIONTRAITS(JINTERFACE_CAN_CALL_VAL2 and JFunctionTraits<CallBack>::arity == 0)

#endif // is c++20

class JInterfaceStrategyPriavte;

class QTECORE_EXPORT JInterfaceStrategy : public QObject
{
	Q_OBJECT

public:
	template <typename...Args>
	explicit JInterfaceStrategy(const QString &className, Args&&...args);

	template <typename...Args>
	explicit JInterfaceStrategy(quint32 user, Args&&...args);

public:
	~JInterfaceStrategy();

public:
	template <typename Result>
	Result call();
	QVariant call();
	void call(std::nullptr_t);

public: // Complex template, please study carefully.
	template <typename CallBack>
	JINTERFACE_CONCEPT_VAR call(CallBack &&callBack);

	template <typename CallBack>
	JINTERFACE_CONCEPT_ANY call(CallBack &&callBack);

	template <typename CallBack>
	JINTERFACE_CONCEPT_IGN call(CallBack &&callBack);

public: // Complex template, please study carefully.
	template <class Class, typename CallBack>
	JINTERFACE_CONCEPT_VAR_OBJ call(Class *obj, CallBack &&callBack);

	template <class Class, typename CallBack>
	JINTERFACE_CONCEPT_ANY_OBJ call(Class *obj, CallBack &&callBack);

	template <class Class, typename CallBack>
	JINTERFACE_CONCEPT_IGN_OBJ call(Class *obj, CallBack &&callBack);

public:
	template <typename Result>
	Result operator()();
	QVariant operator()();
	void operator()(std::nullptr_t);

public: // Complex template, please study carefully.
	template <typename CallBack>
	JINTERFACE_CONCEPT_VAR operator()(CallBack &&callBack);

	template <typename CallBack>
	JINTERFACE_CONCEPT_ANY operator()(CallBack &&callBack);

	template <typename CallBack>
	JINTERFACE_CONCEPT_IGN operator()(CallBack &&callBack);

public: // Complex template, please study carefully.
	template <class Class, typename CallBack>
	JINTERFACE_CONCEPT_VAR_OBJ operator()(Class *obj, CallBack &&callBack);

	template <class Class, typename CallBack>
	JINTERFACE_CONCEPT_ANY_OBJ operator()(Class *obj, CallBack &&callBack);

	template <class Class, typename CallBack>
	JINTERFACE_CONCEPT_IGN_OBJ operator()(Class *obj, CallBack &&callBack);

public:
	bool isValid() const;
	bool deleteOnFinished() const; // default true
	void setDeleteOnFinished(bool enable = true);

protected:
	void setObject(const QString &ifName, QObject *obj);
	void __call(const std::function<void(const QVariant&)> &callBack);
	void __call(const QObject *obj, const std::function<void(const QVariant&)> &callBack);

	template <class Class, typename CallBack>
	typename std::enable_if<not isBaseOfQObject<Class>::value, void>::type inline
	__call(Class *obj, const std::function<void(const QVariant&)> &callBack);

private:
	void __warning(const QString &ifName) const;
	JInterfaceStrategyPriavte *d_ptr = nullptr;
};

/*------------------------------------------------------------------------------------------------------*/

template <typename...Args>
JInterfaceStrategy::JInterfaceStrategy(const QString &className, Args&&...args)
{
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
	int typeID = QMetaType::type(className.toLocal8Bit());
	if( typeID <= 0 )
	{
		__warning(className);
		setObject(className, nullptr);
	}
	else
		setObject(className, QMetaType::metaObjectForType(typeID)->newInstance(J_ARG(args)...));
#else //qt6
	auto metaType = QMetaType::fromName(className.toLocal8Bit());
	if( not metaType.isValid() )
	{
		__warning(className);
		setObject(className, nullptr);
	}
	else
		setObject(className, metaType.metaObject()->newInstance(J_ARG(args)...));
#endif //qt6
}

template<typename...Args>
JInterfaceStrategy::JInterfaceStrategy(quint32 user, Args&&...args) :
	JInterfaceStrategy(QString("ClassName_%1").arg(user), std::forward<Args>(args)...)
{

}

template <typename Result> inline
Result JInterfaceStrategy::call()
{
	return call().value<Result>();
}

inline void JInterfaceStrategy::call(std::nullptr_t)
{
	__call(nullptr);
}

template <typename CallBack>
JINTERFACE_CONCEPT_VAR JInterfaceStrategy::call(CallBack &&callBack)
{
	__call(callBack);
}

template <typename CallBack>
JINTERFACE_CONCEPT_ANY JInterfaceStrategy::call(CallBack &&callBack)
{
	__call([callBack](const QVariant &var) mutable {
		callBack(var.value<typename std::decay<typename JFunctionTraits<CallBack>::template arg<0>::type>::type>());
	});
}

template <typename CallBack>
JINTERFACE_CONCEPT_IGN JInterfaceStrategy::call(CallBack &&callBack)
{
	__call([callBack](const QVariant&) mutable {callBack();});
}

template <class Class, typename CallBack>
JINTERFACE_CONCEPT_VAR_OBJ JInterfaceStrategy::call(Class *obj, CallBack &&callBack)
{
	__call(obj, callBack);
}

template <class Class, typename CallBack>
JINTERFACE_CONCEPT_ANY_OBJ JInterfaceStrategy::call(Class *obj, CallBack &&callBack)
{
	__call(obj, [callBack](const QVariant &var) mutable {
		callBack(var.value<typename std::decay<typename JFunctionTraits<CallBack>::template arg<0>::type>::type>());
	});
}

template <class Class, typename CallBack>
JINTERFACE_CONCEPT_IGN_OBJ JInterfaceStrategy::call(Class *obj, CallBack &&callBack)
{
	__call(obj, [callBack](const QVariant&) mutable {callBack();});
}

template <typename Result> inline
Result JInterfaceStrategy::operator()()
{
	return call<Result>();
}

inline QVariant JInterfaceStrategy::operator()()
{
	return call();
}

inline void JInterfaceStrategy::operator()(std::nullptr_t)
{
	__call(nullptr);
}

template <typename CallBack>
JINTERFACE_CONCEPT_VAR JInterfaceStrategy::operator()(CallBack &&callBack)
{
	call(std::forward<CallBack>(callBack));
}

template <typename CallBack>
JINTERFACE_CONCEPT_ANY JInterfaceStrategy::operator()(CallBack &&callBack)
{
	call(std::forward<CallBack>(callBack));
}

template <typename CallBack>
JINTERFACE_CONCEPT_IGN JInterfaceStrategy::operator()(CallBack &&callBack)
{
	call(std::forward<CallBack>(callBack));
}

template <class Class, typename CallBack>
JINTERFACE_CONCEPT_VAR_OBJ JInterfaceStrategy::operator()(Class *obj, CallBack &&callBack)
{
	call(obj, std::forward<CallBack>(callBack));
}

template <class Class, typename CallBack>
JINTERFACE_CONCEPT_ANY_OBJ JInterfaceStrategy::operator()(Class *obj, CallBack &&callBack)
{
	call(obj, std::forward<CallBack>(callBack));
}

template <class Class, typename CallBack>
JINTERFACE_CONCEPT_IGN_OBJ JInterfaceStrategy::operator()(Class *obj, CallBack &&callBack)
{
	call(obj, std::forward<CallBack>(callBack));
}

template <class Class, typename CallBack>
typename std::enable_if<not isBaseOfQObject<Class>::value, void>::type inline
JInterfaceStrategy::__call(Class *obj, const std::function<void(const QVariant&)> &callBack)
{
	__call(std::bind(callBack, obj, std::placeholders::_1));
}

} //namespace QTE


#endif //JINSTERFACESTRATEGY_H
